package com.travel.admin.config;

import java.io.Writer;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.travel.admin.constants.App;
import com.travel.admin.constants.CacheKey;
import com.travel.admin.constants.Device;
import com.travel.admin.constants.JsonWebToken;
import com.travel.admin.constants.Platform;
import com.travel.admin.constants.Token;
import com.travel.admin.constants.TokenType;
import com.travel.admin.exception.TravelExceptionCode;
import com.travel.admin.util.EncryptUtil;
import com.travel.admin.util.LogSequence;
import com.travel.admin.web.response.HandleResp;

import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.ExpiredJwtException;


public class AuthenticationInterceptor implements HandlerInterceptor {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	@Autowired private RedisTemplate<String, String> redisTemplate;
	private static final String REQUEST_ID = "requestId";
	
	@Override
	public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {
		String requestId = LogSequence.get();
		MDC.put(REQUEST_ID, requestId);
		String platform = request.getHeader(App.HTTP_HEADER_APP_PLATFORM);
		String version = request.getHeader(App.HTTP_HEADER_APP_VERSION);
		String agent = request.getHeader(App.HTTP_HEADER_APP_AGENT);
		if(!StrUtil.isEmpty(platform)){
			logger.info("http header {} : {}",App.HTTP_HEADER_APP_PLATFORM, platform);
		}
		if(!StrUtil.isEmpty(version)){
			logger.info("http header {} : {}",App.HTTP_HEADER_APP_VERSION, version);
		}
		if(!StrUtil.isEmpty(agent)){
			logger.info("http header {} : {}",App.HTTP_HEADER_APP_AGENT, agent);
		}
		String token = request.getHeader(App.HTTP_HEADER_APP_TOKEN);
		try{
			if(!StrUtil.isEmpty(token)){
				JsonWebToken jsonWebToken = EncryptUtil.parseJwt(token);
				logger.info("token id : {}",jsonWebToken.getId());
				logger.info("token type : {}",jsonWebToken.getType());
				logger.info("user id : {}",jsonWebToken.getUserId());
				logger.info("user username : {}",jsonWebToken.getUsername());
//				logger.info("user role : {}",jsonWebToken.getIssuer().substring(0,1));
//				logger.info("user username : {}",jsonWebToken.getSubject());
//				logger.info("user token expired at : {}",jsonWebToken.getExpiration().toString());
//				logger.info("user token issued at : {}",jsonWebToken.getIssuedAt().toString());
				// 判断终端是终端设备类型
				if (Platform.get(platform) == null) {
					// 未携带平台标示, 重新登录
					this.print(request, response,this.getJSON(HandleResp.failedResponse(TravelExceptionCode.TOKEN_INVALID)));
					return false;
				}
				// 终端设备类型
				Device device = Platform.DEVIVCE.get(platform);
				// 无法获取设备类型
				if (device == null) {
					return false;
				}
				logger.info("device : {}", device.name());
//				// 获取凭证类型
				if (TokenType.C.name().equals(jsonWebToken.getType())) {
					// 普通用户
				} else if(TokenType.S.name().equals(jsonWebToken.getType())) {
					// 系统用户
				} else {
					// 未知用户
					return false;
				}
				// 凭证比较,是否为最后一次登录凭证
				String key = CacheKey.TOKEN + jsonWebToken.getType() + device.name() + String.valueOf(jsonWebToken.getUserId());
				String lastToken = this.redisTemplate.opsForValue().get(key);
				logger.info("last token : {}",lastToken);
				if (token.equals(lastToken)) {
					// 凭证相符
				} else {
					// 重新登录
					this.print(request, response,this.getJSON(HandleResp.failedResponse(TravelExceptionCode.TOKEN_INVALID)));
					return false;
				}
				// 更改凭证有效时间
				this.redisTemplate.opsForValue().set(key, token, Platform.TOKEN_EXPIRE.get(platform), Platform.TOKEN_EXPIRE_UNIT.get(platform));
			} else {
				if (handler instanceof HandlerMethod) {
					// 判断接口是否需要凭证
					HandlerMethod handlerMethod = (HandlerMethod) handler;
					Method method = handlerMethod.getMethod();
					if (method.getAnnotation(Token.class) != null) {
						// 重新登录
						this.print(request, response,this.getJSON(HandleResp.failedResponse(TravelExceptionCode.TOKEN_INVALID)));
						return false;
					}
				}
			}
		} catch (Exception e) {
			logger.error(e.getMessage(),e);
			if(e instanceof ExpiredJwtException){
				this.print(request, response,this.getJSON(HandleResp.failedResponse(TravelExceptionCode.TOKEN_INVALID)));
				return false;
			}
		}
		return true;
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
		// 删除请求编号
		MDC.remove(REQUEST_ID);
	}
	
	private void print(HttpServletRequest request,HttpServletResponse response, String data){
		Writer writer = null;
		try{
			response.setContentType(MediaType.APPLICATION_JSON_VALUE + ";charset=" + StandardCharsets.UTF_8);
			writer = response.getWriter();
			writer.write(data);
			writer.flush();
		}catch (Exception e) {
			logger.error(e.getMessage(),e);
		}finally{
			if(writer != null){
				try{
					writer.close();
				}catch (Exception e) {
					logger.error(e.getMessage(),e);
				}
			}
		}
	}
	
	private String getJSON(Object obj){
		ObjectMapper objectMapper = new ObjectMapper();
		try{
			return objectMapper.writeValueAsString(obj);
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	
	Boolean isEmpty(String str){
		if(str != null){
			str = str.trim();
			if(!"".equals(str) && !"null".equalsIgnoreCase(str) && !"undefined".equalsIgnoreCase(str)){
				return false;
			}
		}
		return true;
	}
}
